
import { Action } from './Action'
import { SingleParticle } from './SingleParticle'
import { any, must } from '../core/logic'
import { Vector3 } from '../core/math/Vector3'
import { ShapeType } from "./ParticleType";
import { PositionShape, ShapeTypeEnu, VelocityShape } from "./shape";
import { ParticeGeometry } from "../geom/ParticeGeometry";
import { PointMaterial } from '../material/PointMaterial';
import { Mesh } from '../component/Mesh';
import { Object3D } from '../entity/Object3D';
import { Color } from '../core/math/Color';
import { Engine } from '../Engine';
import { locations } from '../gpu/locations';
import { ParticleComputer } from './ParticleComputer';
import { Base } from '../core/Base';
import { Event, EventType } from '../event/index';
import { ComponentEvent } from '../event/ComponentEvent';
import { ComponentType } from '../component/index';
import { guid, is2DArray } from '../core/utils';

/**
 * 粒子发射器
 */
export class EmitterComputer extends Base {
  constructor(params) {
    super(params)
    this.type = ComponentType.Camera;
    this.event = new ComponentEvent(this.type);
    this.renderSystem = Engine.instance._register._systemManager._systems.get("rendersystem")
    this.position = any(params.position, [0, 0, 0])
    this.positionShape = any(params.positionShape, "Cube")
    this.positionRange = any(params.positionRange, [0, 0, 0])
    this.positionRadius = any(params.positionRadius, 1)
    this.positionconeHeight = any(params.positionconeHeight, 5)
    this.velocity = any(params.velocity, [10, 10, -50])//有方向的三个，如果是圆类型的只用传第一个
    this.velocityShape = any(params.velocityShape, "Cube")
    this.velocityRange = any(params.velocityRange, [50, -20, 50])
    this.velocityRadius = any(params.velocityRadius, 2)
    this.velocityconeHeight = any(params.velocityconeHeight, 4)

    this.acceleration = any(params.acceleration, [0, 0, 0])
    this.angle = any(params.angle, 0)
    this.angleRange = any(params.angleRange, 360)
    this.angleVelocity = any(params.angleVelocity, 0)
    this.texturePath = must(params.texturePath)
    this.size = any(params.size, [[0, 1], [10, 20]])
    this.sizeRange = any(params.sizeRange, 0)
    this.color = any(params.color, [[0, 3], [[0.2, 0.2, 0.2], [0.5, 0.5, 0.5]]])
    this.opacity = any(params.opacity, [[0, 3], [0.8, 1]])
    this.particlesPerSecond = any(params.particlesPerSecond, 100)
    this.duration = any(params.duration, 3)
    this.emitterAge = any(params.emitterAge, 60)
    this.loop = any(params.loop, true)
    this.startTime = performance.now();
    this.alphaCutoff = any(params.alphaCutoff, 0.5)
    this.blendingMode = any(params.blendingMode, 0.5)
    this.age = 0
    this.createBuffer()
    this.initData()
    this.initComputer()
    this.createMesh()
  }

  createBuffer() {
    // 粒子顶点buffer
    // this._particleCount = 4
    this._particleCount = this.particlesPerSecond * Math.min(this.duration, this.emitterAge)
    this._particleInstanceByteSize =
      3 * 4 + // position
      1 * 4 + // size
      3 * 4 + // color
      1 * 4 + // opacity
      3 * 4 + // velocity
      1 * 4 + // age
      4 * 4 // angle
      ;// alive
    this._particlesBuffer = Engine.instance.device.createBuffer({
      label: "_particlesBuffer",
      size: this._particleCount * this._particleInstanceByteSize,
      usage: GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE | GPUBufferUsage | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
    });
    this._particlesBuffer.arrayStride = this._particleInstanceByteSize//顶点步长
    this._particlesBuffer.stepMode = 'instance'//顶点步长
    this._particlesBuffer.vertexNum = this._particleCount
    // 发射器参数buffer
    this._emitterParData = new Float32Array([
      ...this.position, ShapeTypeEnu[this.positionShape],
      ...this.positionRange, this.positionRadius,
      ...this.velocity, ShapeTypeEnu[this.velocityShape],
      ...this.velocityRange, this.velocityRadius,
      ...this.acceleration, this.angle,
      this.angleRange, this.angleVelocity, this.positionconeHeight, this.duration,
      this.velocityconeHeight, 0, 0, 0
    ])
    this._emitterParamsBuffer = Engine.instance.device.createBuffer({
      size: this._emitterParData.byteLength,
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
    });
    Engine.instance.device.queue.writeBuffer(
      this._emitterParamsBuffer,
      0,
      this._emitterParData
    );
    // 帧参数buffer
    this._frameParamsBuffer = Engine.instance.device.createBuffer({
      size: 8 * 4,
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
    });
    this.frameData = new Float32Array([0.016, 0, 0, 0, Math.random() * 100, Math.random() * 100, 1 + Math.random(), 1 + Math.random()])
    //sizes参数buffer
    if (is2DArray(this.size)) {
      this._sizeData = new Float32Array(this.size[1])
    } else {
      this._sizeData = new Float32Array([this.size, this.size])
    }
    this._sizesBuffer = Engine.instance.device.createBuffer({
      size: this._sizeData.byteLength,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
    });
    Engine.instance.device.queue.writeBuffer(
      this._sizesBuffer,
      0,
      this._sizeData
    );
    //sizeTimes参数buffer
    if (is2DArray(this.size)) {
      this._sizeTimes = new Float32Array(this.size[0])
    } else {
      this._sizeTimes = new Float32Array([0, this.duration])
    }
    this._sizesTimesBuffer = Engine.instance.device.createBuffer({
      size: this._sizeTimes.byteLength,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
    });
    Engine.instance.device.queue.writeBuffer(
      this._sizesTimesBuffer,
      0,
      this._sizeTimes
    );
    //colorsTimes参数buffer
    if (is2DArray(this.color)) {
      this._colorsTimes = new Float32Array(this.color[0])
    } else {
      this._colorsTimes = new Float32Array([0, this.duration])
    }
    this._colorsTimesBuffer = Engine.instance.device.createBuffer({
      size: this._colorsTimes.byteLength,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
    });
    Engine.instance.device.queue.writeBuffer(
      this._colorsTimesBuffer,
      0,
      this._colorsTimes
    );
    //colors参数buffer
    if (is2DArray(this.color)) {
      let colorArr = []
      this.color[1].map(item => { colorArr.push(...item, 0) })
      this._colorsData = new Float32Array(colorArr)
    } else {
      this._colorsData = new Float32Array([...this.color, 0, ...this.color, 0])
    }
    this._colorsBuffer = Engine.instance.device.createBuffer({
      size: this._colorsData.byteLength,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
    });
    Engine.instance.device.queue.writeBuffer(
      this._colorsBuffer,
      0,
      this._colorsData
    );
    //opacityTimes参数buffer
    if (is2DArray(this.opacity)) {
      this._opacityTimes = new Float32Array(this.opacity[0])
    } else {
      this._opacityTimes = new Float32Array([0, this.duration])
    }
    this._opacityTimesBuffer = Engine.instance.device.createBuffer({
      size: this._opacityTimes.byteLength,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
    });
    Engine.instance.device.queue.writeBuffer(
      this._opacityTimesBuffer,
      0,
      this._opacityTimes
    );
    // opacity参数buffer
    if (is2DArray(this.opacity)) {
      this._opacityData = new Float32Array(this.opacity[1])
    } else {
      this._opacityData = new Float32Array([this.opacity, this.opacity])
    }
    this._opacityBuffer = Engine.instance.device.createBuffer({
      size: this._opacityData.byteLength,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
    });
    Engine.instance.device.queue.writeBuffer(
      this._opacityBuffer,
      0,
      this._opacityData
    );
  }
  randomValue(min, max) {
    return min + max * (Math.random() - 0.5)
  }
  randomVector3(min, max) {
    const rand3 = new Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5)
    return new Vector3().addVectors(min, new Vector3().multiplyVectors(max, rand3))
  }
  initData() {
    let particleData = []
    for (let index = 0; index < this._particleCount; index++) {
      let position, velocity, angle, size, opacity, color
      if (this.positionShape == ShapeType.Cube) {
        position = this.randomVector3(new Vector3(...this.position), new Vector3(...this.positionRange))
      }
      if (this.positionShape == ShapeType.Sphere) {
        const z = 2 * Math.random() - 1
        const t = Math.PI * 2 * Math.random()
        const r = Math.sqrt(1 - z * z)
        const vec3 = new Vector3(r * Math.cos(t), r * Math.sin(t), z)
        position = new Vector3().addVectors(new Vector3(...this.position), vec3.multiplyScalar(this.positionRadius))
      }
      if (this.positionShape == ShapeType.Circle) {
        const angle = Math.random() * 2 * Math.PI;
        const radius = Math.random() * this.positionRadius;
        position = new Vector3(
          this.position[0] + radius * Math.cos(angle),
          this.position[1] + radius * Math.sin(angle),
          this.position[2] // 圆形平面的z坐标保持不变
        );
      }
      if (this.positionShape == ShapeType.Cone) {
        const height = Math.random() * this.positionconeHeight;
        const radius = (Math.random() * this.positionRadius) * (1 - height / this.positionconeHeight);
        const angle = Math.random() * 2 * Math.PI;

        const x = radius * Math.cos(angle);
        const y = height;
        const z = radius * Math.sin(angle);

        position = new Vector3(x, y, z).add(new Vector3(...this.position));
      }
      if (this.positionShape == ShapeType.Hemisphere) {
        const z = Math.sqrt(1 - (Math.random() * 2 - 1) ** 2); // 限制在半球范围内
        const t = Math.PI * 2 * Math.random();
        const r = Math.sqrt(1 - z * z);
        const vec3 = new Vector3(r * Math.cos(t), r * Math.sin(t), z);
        position = new Vector3().addVectors(new Vector3(...this.position), vec3.multiplyScalar(this.positionRadius));
      }
      if (this.velocityShape == ShapeType.Cube) {
        velocity = this.randomVector3(new Vector3(...this.velocity), new Vector3(...this.velocityRange))
      }
      if (this.velocityShape == ShapeType.Sphere) {
        const direction = new Vector3().subVectors(position, new Vector3(...this.position))
        const speed = this.randomValue(this.velocity[0], this.velocityRadius)
        velocity = direction.normalize().multiplyScalar(speed)
      }
      if (this.velocityShape == ShapeType.Circle) {
        const angle = Math.random() * 2 * Math.PI;
        const speed = this.randomValue(this.velocity[0], this.velocityRadius);
        velocity = new Vector3(
          speed * Math.cos(angle),
          speed * Math.sin(angle),
          0 // 圆形平面的z分量速度设为0
        );
      }
      if (this.velocityShape == ShapeType.Cone) {
        const heightRatio = Math.random();
        const baseRadius = this.velocityRadius * Math.sqrt(heightRatio);
        const angle = Math.random() * 2 * Math.PI;
        const speed = this.randomValue(this.velocity[0], this.velocityRadius);
        const x = speed * baseRadius * Math.cos(angle);
        const y = speed * heightRatio * this.velocityconeHeight;
        const z = speed * baseRadius * Math.sin(angle);

        velocity = new Vector3(x, y, z);
      }
      if (this.velocityShape == ShapeType.Hemisphere) {
        const phi = Math.acos(1 - 2 * Math.random()); // 高度角，限制在半球内
        const theta = Math.random() * 2 * Math.PI; // 方位角
        const speed = this.randomValue(this.velocity[0], this.velocityRadius);

        const x = speed * Math.sin(phi) * Math.cos(theta);
        const y = speed * Math.sin(phi) * Math.sin(theta);
        const z = speed * Math.cos(phi);
        velocity = new Vector3(x, y, z);
      }
      angle = this.randomValue(this.angle, this.angleRange)
      if (is2DArray(this.size)) {
        size = this.size[1][0]
      } else {
        size = this.randomValue(this.size, this.sizeRange)
      }
      if (is2DArray(this.color)) {
        color = this.color[1][0]
      } else {
        color = this.color
      }
      if (is2DArray(this.opacity)) {
        opacity = this.opacity[1][0]
      } else {
        opacity = this.opacity
      }
      var age = Math.random()/2, alive = 0.0
      particleData.push(
        position.x, position.y, position.z, size,
        ...color, opacity,
        velocity.x, velocity.y, velocity.z, age,
        angle, alive, 0, 0)
    }
    const pdata = new Float32Array(particleData)
    Engine.instance.device.queue.writeBuffer(
      this._particlesBuffer,
      0,
      pdata
    );

  }
  initComputer() {
    // 创建存储缓冲区  
    const debugBufferSize = 1024; // 根据需要定义大小  
    this.debugBuffer = Engine.instance.device.createBuffer({
      size: debugBufferSize,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
    });
    this.particleComputer = new ParticleComputer({
      emitterParamsBuffer: this._emitterParamsBuffer,
      frameParamsBuffer: this._frameParamsBuffer,
      sizesBuffer: this._sizesBuffer,
      sizeTimesBuffer: this._sizesTimesBuffer,
      colorsBuffer: this._colorsBuffer,
      colorTimesBuffer: this._colorsTimesBuffer,
      opacitysBuffer: this._opacityBuffer,
      opacityTimesBuffer: this._opacityTimesBuffer,
      particleBuffer: this._particlesBuffer,
      particleNum: this._particleCount,
      id: this.id
    })
    this.renderSystem.computers.push(this.particleComputer)
  }
  // 在计算完成后从缓冲区获取数据并打印  
  async getDebugData() {
    // const readBufferSize = 1024;  
    // const readBuffer = Engine.instance.device.createBuffer({  
    //     size: readBufferSize,  
    //     usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ  
    // });  

    // // 进行计算  
    // const commandEncoder = Engine.instance.device.createCommandEncoder();
    // commandEncoder.copyBufferToBuffer(this.debugBuffer, 0, readBuffer, 0, readBufferSize);  
    // Engine.instance.device.queue.submit([commandEncoder.finish()]);  

    // await readBuffer.mapAsync(GPUMapMode.READ);  
    // const arrayBuffer = readBuffer.getMappedRange();  
    // const result = new Uint32Array(arrayBuffer);  
    // const array = [...result];  
    // console.log(array); // [1, 2, 3, 4, 5] 

    // console.log(result); // 将计算结果打印到控制台  
    // readBuffer.unmap();  
    const readBuffer = Engine.instance.device.createBuffer({
      size: this._particleCount * this._particleInstanceByteSize,
      usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
    });

    // 进行计算  
    const commandEncoder = Engine.instance.device.createCommandEncoder();
    commandEncoder.copyBufferToBuffer(this._particlesBuffer, 0, readBuffer, 0, this._particleCount * this._particleInstanceByteSize);
    Engine.instance.device.queue.submit([commandEncoder.finish()]);

    await readBuffer.mapAsync(GPUMapMode.READ);
    const arrayBuffer = readBuffer.getMappedRange();
    const result = new Float32Array(arrayBuffer);
    const array = [...result];
    console.log(array); // [1, 2, 3, 4, 5] 

    console.log(result); // 将计算结果打印到控制台  
    readBuffer.unmap();
  }
  createMesh() {
    let blend = {
      alphaSrcFactor: "one",
      alphaDstFactor: "zero",
      alphaOperation: "add",
      colorSrcFactor: "one",
      colorDstFactor: "zero",
      colorOperation: "add",
    };
    if (typeof (this.blendingMode) == "string") {
      switch (this.blendingMode) {
        case "NormalBlending":
          blend = {
            alphaSrcFactor: "one",
            alphaDstFactor: "one-minus-src-alpha",
            alphaOperation: "add",
            colorSrcFactor: "src-alpha",
            colorDstFactor: "one-minus-src-alpha",
            colorOperation: "add",
          };
          break;
        case "AdditiveBlending":
          blend = {
            alphaSrcFactor: "one",
            alphaDstFactor: "one",
            alphaOperation: "add",
            colorSrcFactor: "src-alpha",
            colorDstFactor: "one",
            colorOperation: "add",
          };
          break;
        case "SubtractiveBlending":
          blend = {
            alphaSrcFactor: "zero",
            alphaDstFactor: "one",
            alphaOperation: "add",
            colorSrcFactor: "zero",
            colorDstFactor: "one-minus-src",
            colorOperation: "add",
          };
          break;
        case "MultiplyBlending":
          blend = {
            alphaSrcFactor: "zero",
            alphaDstFactor: "src-alpha",
            alphaOperation: "add",
            colorSrcFactor: "zero",
            colorDstFactor: "src",
            colorOperation: "add",
          };
          break;
        default:
          break;
      }
    } else {
      blend = this.blendingMode
    }
    const sample = {
      addressModeU: "clamp-to-edge",
      addressModeV: "clamp-to-edge",
      addressModeW: "clamp-to-edge",
      magFilter: "linear",
      minFilter: "linear",
      mipmapFilter: "linear"
    }
    this._geometry = new ParticeGeometry({ particle: this._particlesBuffer, })
    this._material = new PointMaterial({
      map: {
        sampler: sample,
        url: this.texturePath,
      },
      alphaCutoff: this.alphaCutoff,
      blending:this.blendingMode
      // alphaSrcFactor: blend?.alphaSrcFactor,
      // alphaDstFactor: blend?.alphaDstFactor,
      // alphaOperation: blend?.alphaOperation,
      // colorSrcFactor: blend?.colorSrcFactor,
      // colorDstFactor: blend?.colorDstFactor,
      // colorOperation: blend?.colorOperation,
    })
    this._mesh = new Mesh({ geometry: this._geometry, material: this._material })
    this._object3D = new Object3D({ position: [0, 0, 0], rotation: [0, 0, 0],id:this.id },);
    this._object3D.addComponent(this._mesh);
    Engine.instance.scene.addEntity(this._object3D)
  }
  async update(dt) {
    const currentTime = (performance.now() - this.startTime) / 1000; // 转换为秒  
    var startIndex, endIndex
    if (this.age < this.duration) {
      startIndex = Math.round(this.particlesPerSecond * (this.age + 0))
      endIndex = Math.round(this.particlesPerSecond * (this.age + dt))
      if (endIndex > this._particleCount) {
        endIndex = this._particleCount
      }
    }
    this.frameData[0] = dt
    this.frameData[1] = currentTime
    this.frameData[2] = startIndex || 0
    this.frameData[3] = endIndex || 0
    this.frameData[4] = Math.random() * 100
    this.frameData[5] = Math.random() * 100
    this.frameData[6] = 1 + Math.random()
    this.frameData[7] = 1 + Math.random()
    Engine.instance.device.queue.writeBuffer(
      this._frameParamsBuffer,
      0,
      this.frameData
    );

    this.event.mode = "value";
    this.publish(this.event);
    this.age += dt
    // if(this.age> this.duration){
    //   this.age=0
    // }
  }
}
